/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_cellvideo.h"

/* Helpers */
static void	FASTCALL				Present_NoShader			()
{
	glBindAttribSetSCE(CELL_Video.Shader[0].AttributeSetID);

	glBindTexture(GL_TEXTURE_2D, CELL_Video.Shader[0].TextureID);
	glTextureReferenceSCE(GL_TEXTURE_2D, 1, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, 1, GL_ARGB_SCE, CELL_Video.VideoMode.Width * 4, 0);

	glDrawArrays(GL_QUADS, 0, 4);

	glBindAttribSetSCE(0);
}

#ifdef CELL_SHADER_SUPPORT
#pragma comment (lib,"PSGLcgc")
#pragma comment (lib,"cgc")
static void							ApplyShader					(uint32_t aPass, uint32_t aOutputWidth, uint32_t aOutputHeight, uint32_t aInputWidth, uint32_t aInputHeight, uint32_t aTextureWidth, uint32_t aTextureHeight, uint32_t aFrameCount);


static void FASTCALL				Present_OneShader			()
{
	glBindAttribSetSCE(CELL_Video.Shader[0].AttributeSetID);

	glBindTexture(GL_TEXTURE_2D, CELL_Video.Shader[0].TextureID);
	glTextureReferenceSCE(GL_TEXTURE_2D, 1, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, 1, GL_ARGB_SCE, CELL_Video.VideoMode.Width * 4, 0);

	ApplyShader(0, CELL_Video.ScreenConfiguration.Viewport[2], CELL_Video.ScreenConfiguration.Viewport[3], CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, CELL_Video.ShaderData.PresentCount);
	glDrawArrays(GL_QUADS, 0, 4);

	cgGLDisableProfile(CG_PROFILE_SCE_VP_RSX);
	cgGLDisableProfile(CG_PROFILE_SCE_FP_RSX);

	glBindAttribSetSCE(0);
	CELL_Video.ShaderData.PresentCount ++;
}

static void FASTCALL				Present_TwoShaders			()
{
	//Map Pass 2 buffer as frame buffer
	CELL_PSGL_MapTextureAsFrameBuffer(CELL_Video.Shader[1].TextureID, CELL_Video.VideoMode.Width * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Height * CELL_Video.Shader[0].ScaleFactor);

	//Pass 1
	glBindAttribSetSCE(CELL_Video.Shader[0].AttributeSetID);

	glBindTexture(GL_TEXTURE_2D, CELL_Video.Shader[0].TextureID);
	glTextureReferenceSCE(GL_TEXTURE_2D, 1, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, 1, GL_ARGB_SCE, CELL_Video.VideoMode.Width * 4, 0);

	ApplyShader(0, CELL_Video.VideoMode.Width * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Height * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height, CELL_Video.ShaderData.PresentCount);
	glDrawArrays(GL_QUADS, 0, 4);

	glBindAttribSetSCE(0);

	//Unmap Pass 2 buffer
	CELL_PSGL_UnmapTextureAsFrameBuffer();

	//Pass 2
	glBindAttribSetSCE(CELL_Video.Shader[1].AttributeSetID);

	glBindTexture(GL_TEXTURE_2D, CELL_Video.Shader[1].TextureID);

	ApplyShader(1, CELL_Video.ScreenConfiguration.Viewport[2], CELL_Video.ScreenConfiguration.Viewport[3], CELL_Video.VideoMode.Width * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Height * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Width * CELL_Video.Shader[0].ScaleFactor, CELL_Video.VideoMode.Height * CELL_Video.Shader[0].ScaleFactor, CELL_Video.ShaderData.PresentCount);
	glDrawArrays(GL_QUADS, 0, 4);

	glBindAttribSetSCE(0);
	CELL_Video.ShaderData.PresentCount ++;

	cgGLDisableProfile(CG_PROFILE_SCE_VP_RSX);
	cgGLDisableProfile(CG_PROFILE_SCE_FP_RSX);
}

static void							ApplyShader					(uint32_t aPass, uint32_t aOutputWidth, uint32_t aOutputHeight, uint32_t aInputWidth, uint32_t aInputHeight, uint32_t aTextureWidth, uint32_t aTextureHeight, uint32_t aFrameCount)
{
	if(aPass < 2 && CELL_Video.Shader[aPass].FragmentProgram && CELL_Video.Shader[aPass].VertexProgram)
	{
		cgGLEnableProfile(CG_PROFILE_SCE_VP_RSX);
		cgGLEnableProfile(CG_PROFILE_SCE_FP_RSX);

		cgGLBindProgram(CELL_Video.Shader[aPass].VertexProgram);
		cgGLBindProgram(CELL_Video.Shader[aPass].FragmentProgram);

		if(CELL_Video.Shader[aPass].Projection)			cgGLSetStateMatrixParameter(CELL_Video.Shader[aPass].Projection, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
		if(CELL_Video.Shader[aPass].FragmentVideoSize)	cgGLSetParameter2f(CELL_Video.Shader[aPass].FragmentVideoSize, aInputWidth, aInputHeight);
		if(CELL_Video.Shader[aPass].FragmentTextureSize)cgGLSetParameter2f(CELL_Video.Shader[aPass].FragmentTextureSize, aTextureWidth, aTextureHeight);
		if(CELL_Video.Shader[aPass].FragmentOutputSize)	cgGLSetParameter2f(CELL_Video.Shader[aPass].FragmentOutputSize, aOutputWidth, aOutputHeight);
		if(CELL_Video.Shader[aPass].FragmentFrameCount)	cgGLSetParameter1f(CELL_Video.Shader[aPass].FragmentFrameCount, aFrameCount);
		if(CELL_Video.Shader[aPass].VertexVideoSize)	cgGLSetParameter2f(CELL_Video.Shader[aPass].VertexVideoSize, aInputWidth, aInputWidth);
		if(CELL_Video.Shader[aPass].VertexTextureSize)	cgGLSetParameter2f(CELL_Video.Shader[aPass].VertexTextureSize, aTextureWidth, aTextureHeight);
		if(CELL_Video.Shader[aPass].VertexOutputSize)	cgGLSetParameter2f(CELL_Video.Shader[aPass].VertexOutputSize, aOutputWidth, aOutputHeight);
		if(CELL_Video.Shader[aPass].VertexFrameCount)	cgGLSetParameter1f(CELL_Video.Shader[aPass].VertexFrameCount, aFrameCount);
	}
}


/* Data */
typedef void (FASTCALL*PresentFunction)();
static const PresentFunction PresentFunctions[3] = {Present_NoShader, Present_OneShader, Present_TwoShaders};

/* Implementation */
void								CELL_SHADE_Load				(const char* filename, uint32_t aPass)
{
	const char* args[] = { "-fastmath", "-unroll=all", "-ifcvt=all", 0 };

	if(aPass < 2)
	{
		if(CELL_Video.Shader[aPass].VertexProgram || CELL_Video.Shader[aPass].FragmentProgram)
		{
			cgDestroyProgram(CELL_Video.Shader[aPass].VertexProgram);
			cgDestroyProgram(CELL_Video.Shader[aPass].FragmentProgram);
		}

		CELL_Video.Shader[aPass].VertexProgram = cgCreateProgramFromFile(CELL_Video.PSGLState.ShaderContext, CG_SOURCE, filename, CG_PROFILE_SCE_VP_RSX, "main_vertex", args);
		CELL_Video.Shader[aPass].FragmentProgram = cgCreateProgramFromFile(CELL_Video.PSGLState.ShaderContext, CG_SOURCE, filename, CG_PROFILE_SCE_FP_RSX, "main_fragment", args);

		cgGLEnableProfile(CG_PROFILE_SCE_VP_RSX);
		cgGLEnableProfile(CG_PROFILE_SCE_FP_RSX);

		cgGLBindProgram(CELL_Video.Shader[aPass].VertexProgram);
		cgGLBindProgram(CELL_Video.Shader[aPass].FragmentProgram);

		CELL_Video.Shader[aPass].Projection = cgGetNamedParameter(CELL_Video.Shader[aPass].VertexProgram, "modelViewProj");
		CELL_Video.Shader[aPass].FragmentVideoSize = cgGetNamedParameter(CELL_Video.Shader[aPass].FragmentProgram, "IN.video_size");
		CELL_Video.Shader[aPass].FragmentTextureSize = cgGetNamedParameter(CELL_Video.Shader[aPass].FragmentProgram, "IN.texture_size");
		CELL_Video.Shader[aPass].FragmentOutputSize = cgGetNamedParameter(CELL_Video.Shader[aPass].FragmentProgram, "IN.output_size");
		CELL_Video.Shader[aPass].FragmentFrameCount = cgGetNamedParameter(CELL_Video.Shader[aPass].FragmentProgram, "IN.frame_count");
		CELL_Video.Shader[aPass].VertexVideoSize = cgGetNamedParameter(CELL_Video.Shader[aPass].VertexProgram, "IN.video_size");
		CELL_Video.Shader[aPass].VertexTextureSize = cgGetNamedParameter(CELL_Video.Shader[aPass].VertexProgram, "IN.texture_size");
		CELL_Video.Shader[aPass].VertexOutputSize = cgGetNamedParameter(CELL_Video.Shader[aPass].VertexProgram, "IN.output_size");
		CELL_Video.Shader[aPass].VertexFrameCount = cgGetNamedParameter(CELL_Video.Shader[aPass].VertexProgram, "IN.frame_count");
	}

	if(CELL_Video.Shader[0].VertexProgram && CELL_Video.Shader[0].FragmentProgram)
	{
		CELL_Video.ShaderData.Passes = (CELL_Video.Shader[1].VertexProgram && CELL_Video.Shader[1].FragmentProgram) ? 2 : 1;
	}
	else
	{
		CELL_Video.ShaderData.Passes = 0; 
	}
}

void								CELL_SHADE_UpdateMode		()
{
	uint32_t scale_width = CELL_Video.VideoMode.Width * CELL_Video.Shader[0].ScaleFactor;
	uint32_t scale_height = CELL_Video.VideoMode.Height * CELL_Video.Shader[0].ScaleFactor;

	if(CELL_Video.ShaderData.Passes == 0)
	{
		CELL_PSGL_MakeAttribSetRect(&CELL_Video.Shader[0].VertexBufferID, &CELL_Video.Shader[0].AttributeSetID, 0, 0, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height);

		if(!CELL_Video.Shader[0].TextureID)
		{
			CELL_Video.Shader[0].TextureID = CELL_PSGL_MakeTexture();
		}
	}
	else
	{
		for(int i = 0; i != CELL_Video.ShaderData.Passes; i ++)
		{
			if(CELL_Video.Shader[i].FragmentProgram && CELL_Video.Shader[i].VertexProgram)
			{
				if(!CELL_Video.Shader[i].TextureID)
				{
					CELL_Video.Shader[i].TextureID = CELL_PSGL_MakeTexture();
				}

				glBindTexture(GL_TEXTURE_2D, CELL_Video.Shader[i].TextureID);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, CELL_Video.Shader[i].Smooth ? GL_LINEAR : GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, CELL_Video.Shader[i].Smooth ? GL_LINEAR : GL_NEAREST);

				if(i == 1)
				{
					glTexImage2D(GL_TEXTURE_2D, 0, GL_ARGB_SCE, scale_width, scale_height, 0, GL_ARGB_SCE, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
					CELL_PSGL_MakeAttribSetRectI(&CELL_Video.Shader[i].VertexBufferID, &CELL_Video.Shader[i].AttributeSetID, 0, 0, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height);
				}
				else
				{
					CELL_PSGL_MakeAttribSetRect(&CELL_Video.Shader[i].VertexBufferID, &CELL_Video.Shader[i].AttributeSetID, 0, 0, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height);
				}
			}
		}
	}
}
#else
void								CELL_SHADE_UpdateMode		()
{
	CELL_PSGL_MakeAttribSetRect(&CELL_Video.Shader[0].VertexBufferID, &CELL_Video.Shader[0].AttributeSetID, 0, 0, CELL_Video.VideoMode.Width, CELL_Video.VideoMode.Height);

	if(!CELL_Video.Shader[0].TextureID)
	{
		CELL_Video.Shader[0].TextureID = CELL_PSGL_MakeTexture();
	}
}
#endif

void								CELL_SHADE_Present			()
{
#ifdef CELL_SHADER_SUPPORT
	PresentFunctions[CELL_Video.ShaderData.Passes]();
#else
	Present_NoShader();
#endif
}

